/*  
 * i-OSGi - Tunable Bundle Isolation for OSGi
 * Copyright (C) 2011  Sven Schulz
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.iosgi.ie.process;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.lang.reflect.Proxy;
import java.net.SocketException;
import java.net.URI;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.ServiceProperty;
import org.iosgi.Constants;
import org.iosgi.IsolatedFramework;
import org.iosgi.IsolationEnvironment;
import org.iosgi.impl.EnvironmentIDs;
import org.iosgi.util.io.Files;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.ethz.iks.r_osgi.RemoteOSGiService;

/**
 * @author Sven Schulz
 */
@Component(immediate = true)
@Provides(specifications = { IsolationEnvironment.class,
		IsolatedFramework.class })
public class ProcessIE implements IsolationEnvironment, IsolatedFramework {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(ProcessIE.class);

	private static final ExecutorService EXEC_SVC = Executors
			.newSingleThreadExecutor();

	private static BundleContext context;

	static BundleContext getBundleContext() {
		return context;
	}

	@ServiceProperty(name = "environment.id")
	private URI id;

	@SuppressWarnings("unused")
	@ServiceProperty(name = Constants.R_OSGI_REGISTRATION)
	private boolean publishRemotely = Boolean.TRUE;

	@SuppressWarnings("unused")
	@ServiceProperty(name = Constants.LEVEL)
	private int level = 0;

	public ProcessIE(BundleContext context) throws SocketException {
		id = EnvironmentIDs.getEnvironmentId(1);
		ProcessIE.context = context;
	}

	public URI getId() {
		return id;
	}

	public synchronized void destroy() {
		LOGGER.debug("destroying process IE {}", id);
		
		/*
		 * Preload the class at it won't be available any more, when the
		 * shutdown hook is executed.
		 */
		try {
			this.getClass().getClassLoader().loadClass(Files.class.getName());
		} catch (ClassNotFoundException cnfe) {
			assert false;
		}
		final long p = EnvironmentIDs.getLastPart(id);
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
			@Override
			public void run() {
				File rootDir = new File(new File("."), "work" + File.separator
						+ p);
				if (!Files.delete(rootDir)) {
					LOGGER.warn("could not delete all files for process IE {}",
							id);
				}
			}
		}));
		EXEC_SVC.submit(new Callable<Void>() {
			@Override
			public Void call() throws Exception {
				synchronized (ProcessIE.this) {
					Bundle framework = context.getBundle(0);
					framework.stop(0);
				}
				return null;
			}
		});
	}

	@Override
	public Bundle installBundle(String location, byte[] data)
			throws BundleException {
		/* 1. Install bundle locally. */
		Bundle b = context.installBundle(location, new ByteArrayInputStream(
				data));

		/* 2. Export as proxied remote service. */
		Bundle l = (Bundle) Proxy.newProxyInstance(this.getClass()
				.getClassLoader(), new Class[] { Bundle.class },
				new LocalBundle(b));
		Dictionary<String, Object> props = new Hashtable<String, Object>();
		props.put(Constants.BUNDLE_ID, b.getBundleId());
		props.put(RemoteOSGiService.R_OSGi_REGISTRATION, Boolean.TRUE);
		context.registerService(Bundle.class.getName(), l, props);

		/* 3. Return remote proxy that performs lookup. */
		Bundle p = (Bundle) Proxy.newProxyInstance(this.getClass()
				.getClassLoader(), new Class[] { Bundle.class },
				new RemoteBundle(b.getBundleId()));
		return p;
	}

}